/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package mapdbtest;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.ResourceBundle;
import java.util.concurrent.ConcurrentNavigableMap;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.Slider;
import javafx.scene.input.MouseEvent;
import javafx.stage.Stage;
import javafx.util.StringConverter;
import javafx.util.converter.NumberStringConverter;
import mapdbtest.NoteEditViewController.NoteEditCallback;
import org.mapdb.DB;
import org.mapdb.DBMaker;

/**
 *
 * @author Arttu
 */
public class FXMLDocumentController implements Initializable, NoteEditCallback {

    private Label label;
    @FXML
    private ListView<Note> noteListView;
    @FXML
    private Button addButton;
    @FXML
    private Button clearButton;
    @FXML
    private Button generateButton;
    @FXML
    private Slider randomAmountSlider;

    private ObservableList<Note> notes;

    private ConcurrentNavigableMap<Integer, Note> databaseMap;
    private DB db;
    @FXML
    private Label sliderValue;

    @Override
    public void initialize(URL url, ResourceBundle rb) {

        notes = FXCollections.observableList(new LinkedList<Note>());

        initDatabase();
        loadNotesFromDatabase(notes);
         
        StringConverter<Number> converter = new NumberStringConverter();
        Bindings.bindBidirectional(sliderValue.textProperty(), randomAmountSlider.valueProperty(), converter);
        
        noteListView.setItems(notes);
    }

    private void loadNotesFromDatabase(ObservableList<Note> notes) {

        notes.clear();
        for (Note note : databaseMap.values()) {
            notes.add(note);
        }

    }

    private void initDatabase() {
        db = DBMaker.newFileDB(new File("notesdb"))
                .closeOnJvmShutdown()
                .encryptionEnable("password")
                .make();
        
        
        databaseMap = db.getTreeMap("notes");
        if (databaseMap == null) {
            databaseMap = db.createTreeMap("notes").valueSerializer(new NoteSerializer()).make();
        }

    }

    @FXML
    private void handleAddButtonAction(ActionEvent event) {
        ObservableNote note = new ObservableNote();
        note.setId(getNextId());
        note.setHeading("");
        note.setNote("");
        openNoteEditView(note);
    }

    private void openNoteEditView(ObservableNote note) {
        try {
            Parent root;
            FXMLLoader loader = new FXMLLoader(getClass().getResource("NoteEditView.fxml"));

            root = loader.load();
            Stage stage = new Stage();
            stage.setTitle("Note edit");
            stage.setScene(new Scene(root));

            NoteEditViewController controller = loader.<NoteEditViewController>getController();
            controller.setNote(note);
            controller.setCallback(this);

            stage.show();

            //hide this current window (if this is whant you want
            //((Node)(event.getSource())).getScene().getWindow().hide();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @FXML
    private void handleClearButtonAction(ActionEvent event) {

        databaseMap.clear();
        db.commit();
        loadNotesFromDatabase(notes);

    }

    @FXML
    private void handleGenerateButtonAction(ActionEvent event) {

        int count = (int) randomAmountSlider.getValue();
        List<Note> newNotes = new ArrayList<>(count);
        int id = getNextId();
        for (int ii = 0; ii < count; ii++) {

            Note note = new Note(id, "Heading " + id, "Note " + id);
            newNotes.add(note);

            id ++;
        }
        notes.addAll(newNotes);
        commitToDatabase(newNotes);
    }

    private int getNextId() {
        int max = 0;
        for (Note note : databaseMap.values()) {
            if (note.getId() > max) {
                max = note.getId();
            }
        }
        max++;
        return max;
    }

    @Override
    public void noteEditFinished(ObservableNote note) {

        Platform.runLater(new Runnable() {
            @Override
            public void run() {
                updateUiAfterAddition(new Note(note.getId(), note.getHeading(), note.getNote()));
            }
        });

    }

    private void updateUiAfterAddition(Note note) {
        if (isNewNote(note)) {

            commitToDatabase(note);
            notes.add(note);

        } else {

            commitToDatabase(note);
            for (int ii = 0; ii < notes.size(); ii++) {
                if (notes.get(ii).getId() == note.getId()) {
                    notes.remove(ii);
                    notes.add(ii, note);
                    break;
                }
            }
        }
    }

    private void commitToDatabase(Note note) {
        databaseMap.put(note.getId(), note);
        db.commit();
    }

    private boolean isNewNote(Note note) {
        return !databaseMap.containsKey(note.getId());
    }

    @Override
    public void noteEditCanceled(ObservableNote note) {
    }

    @FXML
    private void handleListMouseClick(MouseEvent event) {

        if (event.getClickCount() > 1) {
            Note note = noteListView.getSelectionModel().getSelectedItem();
            ObservableNote observable = new ObservableNote();
            observable.setId(note.getId());
            observable.setHeading(note.getHeading());
            observable.setNote(note.getNote());
            openNoteEditView(observable);
        }

    }

    private void commitToDatabase(List<Note> newNotes) {
        for (Note note : newNotes) {
            System.out.println("Saving " + note.getId());
            databaseMap.put(note.getId(), note);
        }
        db.commit();
    }

}
